package pyip.lib.mixtool;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * TODO<br>
 * jsurl, cssurl 的匹配逻辑有待完善<br>
 * 对于非常规写法的js css是无法匹配到相关url的, 如百度首页, 从下载得到的html看不到任何css文件的url, 估计是通过js动态加载的<br>
 * <br>
 * 调用方式: java className url path [type] [charset]<br>
 * <br>
 * type 可以是 all js css, 默认 all, 不区分大小写<br>
 * charset 默认是utf-8<br>
 * <br>
 * 根url对应的页面会被保存为page.html
 * @author yepeng
 * @date 2015年3月24日
 */
public class JSCSSDownloader {
    private static String baseUrl = null;

    private static String baseFilePath = null;
    private static final String FILE_SEPARATOR = "\\";

    private static final String TYPE_ALL = "all";
    private static final String TYPE_JS = "js";
    private static final String TYPE_CSS = "css";

    private static final String ROOT_URL_FILENAME = "page.html";

    public static void main(String[] args) {
        System.out.println("application started");
        System.out.println();

        String url = null;
        String type = TYPE_ALL;
        String charset = "utf-8";

        if (args.length >= 1) {
            url = args[0];
        }
        if (args.length >= 2) {
            baseFilePath = args[1];
        }
        if (args.length >= 3) {
            type = args[2];
        }
        if (args.length >= 4) {
            charset = args[3];
        }

        if (isBlank(url)) {
            throw new IllegalArgumentException("参数url不能为空");
        }
        if (isBlank(baseFilePath)) {
            throw new IllegalArgumentException("参数path不能为空");
        }

        // 下载根页面
        String html = null;
        try {
            html = getString(url, charset);

            Pattern basePattern = Pattern.compile("<base[\\s]+href=[\"|\'](.*?)[\"|\'][\\s\\S]*?>", Pattern.CASE_INSENSITIVE);
            Matcher baseMatcher = basePattern.matcher(html);
            if (baseMatcher.find()) {
                baseUrl = baseMatcher.group(1).trim();
            }
            if (isBlank(baseUrl)) {
                baseUrl = url.substring(0, url.indexOf("/", "https://".length()));
            }
            if (!baseUrl.endsWith("/")) {
                baseUrl = baseUrl + "/";
            }
        } catch (IOException e) {
            System.err.println("下载html页面失败");
            e.printStackTrace();
            return;
        }

        // 存储根页面
        try {
            storeFile(html, ROOT_URL_FILENAME);
        } catch (IOException e) {
            System.err.println("储存html页面失败");
            e.printStackTrace();
            return;
        }

        // 解析js css url
        System.out.println();
        System.out.println("parsing urls -->");
        List<String> jsUrls = null;
        List<String> cssUrls = null;
        if (type.equalsIgnoreCase(TYPE_ALL) || type.equalsIgnoreCase(TYPE_JS)) {
            jsUrls = getJsUrl(html);
        }
        if (type.equalsIgnoreCase(TYPE_ALL) || type.equalsIgnoreCase(TYPE_CSS)) {
            cssUrls = getCssUrl(html);
        }
        System.out.println();

        // 下载并存储js css
        System.out.println("downloading resources -->");
        if (type.equalsIgnoreCase(TYPE_ALL) || type.equalsIgnoreCase(TYPE_JS)) {
            processResources(jsUrls, TYPE_JS, charset);
        }
        if (type.equalsIgnoreCase(TYPE_ALL) || type.equalsIgnoreCase(TYPE_CSS)) {
            processResources(cssUrls, TYPE_CSS, charset);
        }

        System.out.println();
        System.out.println("application stopped");
    }

    private static List<String> getJsUrl(String html) {
        List<String> urls = new ArrayList<>();
        // normal js url
        Pattern jsPattern = Pattern.compile("<script[\\s\\S]*?src=[\"|'](.*?)[\"|'][\\s\\S]*?>", Pattern.CASE_INSENSITIVE);
        Matcher jsMatcher = jsPattern.matcher(html);
        while (jsMatcher.find()) {
            String jsSrc = jsMatcher.group(1);
            if (jsSrc.toLowerCase().endsWith(".js")) {
                String fullUrl = mergeUrl(jsSrc);
                urls.add(fullUrl);
                System.out.println(jsSrc + "  -->  " + fullUrl);
            }
        }

        // data-main js url, 如requireJs的加载方式
        Pattern dataMainPattern = Pattern.compile("<script[\\s\\S]*?data-main=[\"|'](.*?)[\"|'][\\s\\S]*?>", Pattern.CASE_INSENSITIVE);
        Matcher dataMainMatcher = dataMainPattern.matcher(html);
        while (dataMainMatcher.find()) {
            String jsSrc = dataMainMatcher.group(1);
            String fullUrl = mergeUrl(jsSrc);
            if (!fullUrl.endsWith(TYPE_JS)) {
                fullUrl = fullUrl + "." + TYPE_JS;
            }
            urls.add(fullUrl);
            System.out.println(jsSrc + "  -->  " + fullUrl);
        }
        return urls;
    }

    private static List<String> getCssUrl(String html) {
        List<String> urls = new ArrayList<>();
        Pattern cssPattern = Pattern.compile("<link[\\s\\S]*?href=[\"|'](.*?)[\"|'][\\s\\S]*?>", Pattern.CASE_INSENSITIVE);
        Matcher cssMatcher = cssPattern.matcher(html);
        while (cssMatcher.find()) {
            String cssSrc = cssMatcher.group(1);
            if (cssSrc.toLowerCase().endsWith(".css")) {
                String fullUrl = mergeUrl(cssSrc);
                urls.add(fullUrl);
                System.out.println(cssSrc + " --> " + fullUrl);
            }
        }
        return urls;
    }

    private static void processResources(List<String> urls, String resourceType, String charset) {
        for (String url : urls) {
            String resource = null;
            try {
                resource = getString(url, charset);
            } catch (IOException e) {
                System.err.println("下载html页面上的" + resourceType + "失败, url: " + url);
                e.printStackTrace();
                return;
            }

            String fileName = null;
            try {
                int index = url.lastIndexOf("/");
                if (index == -1) {
                    index = url.lastIndexOf("\\");
                }
                fileName = url.substring(index + 1);
                if (!fileName.endsWith(resourceType)) {
                    fileName = fileName + "." + resourceType;
                }
                storeFile(resource, fileName);
            } catch (IOException e) {
                System.err.println("存储下载的的" + resourceType + "失败, url: " + url + ", path: " + fileName);
                e.printStackTrace();
                return;
            }

        }
    }

    private static String mergeUrl(String url) {
        if (url.toLowerCase().startsWith("http")) {
            return url;
        }
        if (url.startsWith("/")) {
            url = url.substring(1, url.length());
        }
        return baseUrl + url;
    }

    private static String getString(String url, String chartset) throws IOException {
        StringBuilder sb = new StringBuilder();

        System.out.println("downloading: " + url);
        URL _url = new URL(url);
        InputStream in = null;
        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) _url.openConnection();
            in = connection.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(in, chartset));
            String line = null;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (connection != null) {
                connection.disconnect();
            }
        }

        return sb.toString();
    }

    private static void storeFile(String content, String path) throws IOException {
        if (path.startsWith("/")) {
            path = path.substring(1, path.length());
        }
        path = baseFilePath + FILE_SEPARATOR + path;

        BufferedWriter bw = null;
        FileWriter writer = null;
        try {
            File file = new File(path);
            if (!file.exists()) {
                file.getParentFile().mkdirs();
                file.createNewFile();
            }
            writer = new FileWriter(file);
            bw = new BufferedWriter(writer);
            bw.write(content);
            bw.flush();
        } finally {
            if (writer != null) {
                writer.close();
            }
            if (bw != null) {
                bw.close();
            }
        }
    }

    /**
     * copy from apache commons lang StringUtils
     * @param cs
     * @return
     */
    private static boolean isBlank(final CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (Character.isWhitespace(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }
}
